// i.d(t, { Q: () => a, Y: () => MessageBus })

import TypeLookup from "../utils/TypeLookup"
import Logger from "../utils/Logger"
import { createSubscription } from "../utils/Subscription"
import funcUtil from "../utils/func.util"
import Message from "./message"

const o = new Logger("message-bus")

export const messageType = {
  PERMANENT: 0,
  ONCE: 1,
  0: "PERMANENT",
  1: "ONCE"
}

export default class MessageBus {
  typeLookup: TypeLookup
  callbacks: Record<string, Array<{ type: number; callback: Function }>>
  messageQueue: Array<Message>
  messageCount: number
  constructor() {
    this.typeLookup = new TypeLookup()
    this.callbacks = {}
    this.messageQueue = []
    this.messageCount = 0
  }
  subscribe<A extends typeof Message = typeof Message>(e: A, t: (e?: A["prototype"]) => any, i = messageType.PERMANENT) {
    return createSubscription(
      () => this._subscribe(e, t, i),
      () => this.unsubscribe(e, t),
      !0,
      funcUtil.getClassName(e)
    )
  }
  _subscribe(e, t, i = messageType.PERMANENT) {
    const n = this.typeLookup.getKeyByType(e, !0)
    this.callbacks[n] || (this.callbacks[n] = []), this.callbacks[n].push({ type: i, callback: t })
  }
  unsubscribe(e, t) {
    const i = this.typeLookup.getKeyByType(e)
    i ? this.unsubscribeByIndex(i, t) : o.debug(`Message callback not bound for Type: ${e.name}`)
  }
  unsubscribeByIndex(e, t) {
    const i = this.callbacks[e].findIndex(e => e.callback === t)
    ;-1 !== i ? this.callbacks[e].splice(i, 1) : o.debug(`Message callback not bound for TypeIndex: ${e}`)
  }
  broadcast<A extends Message = Message>(e: A) {
    if ((e.setID(this.messageCount++), this.messageQueue.push(e), 1 === this.messageQueue.length)) {
      for (const e of this.messageQueue) {
        const t = this.typeLookup.getKeyByInstance(e)
        if (!t) continue
        const i = this.callbacks[t].slice()
        for (const n of i) n.callback(e), n.type === messageType.ONCE && this.unsubscribeByIndex(t, n.callback)
      }
      this.messageQueue = []
    }
  }
}
